package com.starmark.core.framework.support.lock.aop;


import com.starmark.core.framework.support.lock.anno.DLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * Description: 分布式锁
 * <p>
 * 先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试)
 * 如果没有重试次数,方法返回null 记得捕获NP
 * 当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔
 * </p>
 */
@Aspect
@Component
@Slf4j
public class DLockAspect {

    private static final String LOCK_NAME = "lockName";
    private static final String RETRY_TIMES = "retryTimes";
    private static final String RETRY_WAIT = "retryWait";

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Pointcut("@annotation(com.starmark.core.framework.support.lock.anno.DLock)")
    public void dLockAspect() {
    }

    @Around("dLockAspect()")
    public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable {
        //获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
        assert annotationArgs != null;
        String lockName = (String) annotationArgs.get(LOCK_NAME);
        Assert.notNull(lockName, "分布式,锁名不能为空");
        long retryWait = (long) annotationArgs.get(RETRY_WAIT);

        Lock lock = redisLockRegistry.obtain(lockName);
        if (lock.tryLock(retryWait, TimeUnit.SECONDS)) {
            try {
                return proceeding.proceed();
            } finally {
                lock.unlock();
            }
        }
        return null;
    }


    /**
     * 获取锁参数
     *
     * @param proceeding
     * @return
     */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Map<String, Object> result = new HashMap<String, Object>();
                DLock redisLock = method.getAnnotation(DLock.class);
                String firstArg = getFirstArg(proceeding);
                assert firstArg != null;
                result.put(LOCK_NAME, redisLock.lockName().concat("_").concat(firstArg));
                result.put(RETRY_TIMES, redisLock.retryTimes());
                result.put(RETRY_WAIT, redisLock.retryWait());
                return result;
            }
        }
        return null;
    }

    /**
     * 获取第一个String类型的参数为锁的业务参数
     *
     * @param proceeding
     * @return
     */
    private String getFirstArg(ProceedingJoinPoint proceeding) {
        Object[] args = proceeding.getArgs();
        if (args != null && args.length > 0) {
            for (Object object : args) {
                String type = object.getClass().getName();
                if ("java.lang.String".equals(type)) {
                    return (String) object;
                }
            }
        }
        return null;
    }

}